본문으로 건너뛰기

useSyncExternalStore

🧑🏻‍💻 useSyncExternalStore


useSyncExternalStore 는 외부 데이터 저장소를 구독할 수 있는 React 훅이다.

✅ useSyncExternalStore 문법

const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
  • subscribe 함수는 하나의 callback 인수를 받아 데이터 저장소를 구독해야 하고, 구독 취소 함수를 반환해야 한다.
  • getSnapshot 함수는 데이터 저장소에서 데이터의 스냅샷을 읽어야 한다.
  • getServerSnapshot 는 데이터 저장소에 있는 데이터의 초기 스냅샷을 반환하는 함수이다.
  • React는 이 스냅샷를 사용해 컴포넌트가 스토어를 구독한 상태로 유지하고 변경 사항이 있을 때 다시 렌더링한다.

🧑🏻‍💻 참고하기


✅ useSyncExternalStore를 언제 사용해야 하는가?

  • React 외부의 저장소에서 시간이 지남에 따라 변경되는 데이터를 읽어야 하는 경우
  • 변이 가능한 값을 노출하는 브라우저 API와 그 변이 사항을 구독하는 이벤트를 사용할 때
  • React 외부에 state를 보관하는 서드파티 상태 관리 라이브러리를 사용할 때
  • 가능하면 React 빌트인 state는 useStateuseReducer와 함께 사용하고 기존의 비 React 코드와 통합해야 할 때는 useSyncExternalStore를 사용하자.

✅ 선택형 인수인 getServerSnapshot 는 언제 사용해야 하는가?

  • React 앱이 서버 렌더링을 사용하는 경우에 아래 상황이 발생할 때 사용한다.
    → 브라우저 전용 API에 연결하는 경우 (서버에 해당 API가 존재하지 않음)
    → 타사 데이터 저장소에 연결하는 경우 (서보와 클라이언트 간 데이터 일치 문제)
  • 참고로 getServerSnapshot는 HTML을 생성할 때 서버에서 실행된다. 즉 React가 서버 HTML을 가져와서 인터랙티브하게 만들 때(hydration) 클라이언트에서 실행된다.

✅ 일반적인 사용 방법

import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';

function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
// ...
}
  • 컴포넌트의 최상위 레벨에서 호출하여 외부 데이터 저장소에서 값을 읽는다.
  • 사용자 정의 훅에서 호출하여 사용하면 기능 구현 반복을 막을 수 있다.

✅ 주의 사항

  • React 앱이 서버 렌더링을 사용하는 경우 이 함수가 제공되지 않으면 서버에서 컴포넌트를 렌더링할 때 오류가 발생한다.

  • 스토어 데이터가 변이 가능한 경우 getSnapshot 함수는 해당 데이터의 불변 스냅샷을 반환해야 한다.

  • 리렌더링 시에 다른 subscribe 함수가 전달되면 React는 스토어를 다시 구독하는 문제가 있다. 이를 방지하기 위해서 컴포넌트 외부에서 subscribe를 선언하자.

    function ChatIndicator() {
    const isOnline = useSyncExternalStore(subscribe, getSnapshot);
    // ...
    }

    // ✅ 항상 동일한 함수이므로 React는 이를 재구독할 필요가 없습니다
    function subscribe() {
    // ...
    }

🧑🏻‍💻 활용 및 생각할 거리


✅ 브라우저 API 구독하기 (ex. 네트워크 연결 상태)

import { useSyncExternalStore } from 'react';

function ChatIndicator() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot);

return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}

// snapShot 값 읽어어는 함수
// 브라우저 API에서 현재 값을 읽기
function getSnapshot() {
return navigator.onLine;
}

// 브라우저 API 데이터 저장소 구현
// 이벤트 구독 및 취소를 해주어야 한다.
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}

✅ 외부 데이터 저장소가 업데이트 되면 리렌더링 하기

import { useSyncExternalStore } from 'react';

// 서드 파티 라이브러리에서 제공해주는 객체
import { todosStore } from './todoStore.js';

// 커스텀 훅
export default function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
return (
<>
<button onClick={() => todosStore.addTodo()}>Add todo</button>
<hr />
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</>
);
}